/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package czStvDpds.graphics3D;

import czStvDpds.graphics3D.geometry.Texture;
import czStvDpds.graphics3D.geometry.TextureProperties;
import czStvDpds.logging.Logging;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import org.lwjgl.BufferUtils;

/**
 *
 * @author divad
 */
public class TMMLoader {

    private Texture[] colorTextures;
    private Texture maskTexture;
    public static final int NOT_LOADED = 1, FULLY_LOADED = 2, PARTIALLY_LOADED = 3;
    private int state = NOT_LOADED;
    private HashMap<Integer, ImageInfo> imgs;
    private File sourceFile;

    public void loadFile(File tmmFile) {
        this.sourceFile = null;
        this.colorTextures = null;
        this.maskTexture = null;
        this.imgs = new HashMap();
        if (tmmFile != null && tmmFile.exists()) {
            this.sourceFile = tmmFile;
            String data = new String(this.loadData(tmmFile));
            if (data != null) {
                data = this.removeComments(data);
                data.replace("\r", "");
                String[] parts = this.separateParts(data);
                for (int i = 0; i < parts.length; i++) {
                    String[] lines = separateLines(parts[i]);
                    this.getData(lines);
                }
            }
            if (this.colorTextures == null || this.maskTexture == null) {
                this.state = PARTIALLY_LOADED;
            } else {
                this.state = FULLY_LOADED;
            }
        } else {
            this.state = NOT_LOADED;
        }
    }

    public void generateAllTextures() {
        if (this.state != TMMLoader.NOT_LOADED) {
            if (colorTextures != null) {
                for (Texture t : colorTextures) {
                    GraphicsIndex.getGLSynchronizer().generateTexture(t);
                }
            }
            if (maskTexture != null) {
                GraphicsIndex.getGLSynchronizer().generateTexture(maskTexture);
            }
        }
    }

    public Texture[] getColorTextures() {
        return colorTextures;
    }

    public Texture getMaskTexture() {
        return maskTexture;
    }

    public File getSourceFile() {
        return sourceFile;
    }

    public int getState() {
        return state;
    }

///////////////////////načítání//////////////////////////
    private void getData(String[] lines) {
        try {
            if (lines[0].compareTo("IMGS") == 0) {
                this.imgs = this.readImgs(lines);
            } else if (lines[0].compareTo("COLORMAPS") == 0) {
                this.colorTextures = this.getColorTextures(lines);
            } else if (lines[0].compareTo("ALPHAMASK") == 0) {
                this.maskTexture = this.getAlphaMask(lines[1]);
            }
        } catch (Exception ex) {
            Logging.getMainLogger().log(Level.WARNING, "Material loading problem:", ex);
            ex.printStackTrace();
        }
    }

    private Texture getTexture(ImageInfo ii) throws Exception {
        Texture t = null;
        if (ii.getData().exists()) {
            t = new Texture();
            TextureProperties tp = new TextureProperties();
            tp.setType(ii.getType());
            tp.setFormat(ii.getFormat());
            t.setProperties(tp);

            if (ii.getType() == TextureProperties.TEXTURE_1D || ii.getType() == TextureProperties.TEXTURE_2D) {
                BufferedImage img = ImageIO.read(ii.getData());
                ByteBuffer data = TextureCreator.getImageData(img);
                tp.setHeight(img.getHeight());
                tp.setWidth(img.getWidth());
                t.setData(data);
            } else if (ii.getType() == TextureProperties.TEXTURE_3D) {
                BufferedImage img = ImageIO.read(ii.getData());
                ByteBuffer data = null;
                if (img.getHeight() % img.getWidth() == 0) {
                    tp.setDepth(img.getHeight() / img.getWidth());
                    tp.setWidth(img.getWidth());
                    tp.setHeight(img.getHeight() / tp.getDepth());
                    data = TextureCreator.getImageData(img);
                } else if (img.getWidth() % img.getHeight() == 0) {
                    tp.setDepth(img.getWidth() / img.getHeight());
                    tp.setWidth(img.getWidth() / tp.getDepth());
                    tp.setHeight(img.getHeight());
                    BufferedImage[] imgsData = new BufferedImage[tp.getDepth()];
                    for (int i = 0; i < imgsData.length; i++) {
                        imgsData[i] = img.getSubimage(tp.getWidth() * i, 0, tp.getWidth(), tp.getHeight());
                    }
                    data = TextureCreator.getMultiImageData(imgsData);

                } else {
                    data = BufferUtils.createByteBuffer(4);
                    data.put((byte) 255);
                    data.put((byte) 255);
                    data.put((byte) 255);
                    data.put((byte) 255);
                    data.rewind();
                    tp.setWidth(1);
                    tp.setHeight(1);
                    tp.setDepth(1);
                }
                t.setData(data);
            } else if (ii.getType() == TextureProperties.TEXTURE_CUBE_MAP) {
                BufferedImage img = ImageIO.read(ii.getData());
                ByteBuffer data = null;
                tp.setDepth(6);
                if (img.getWidth() % 4 == 0 && img.getHeight() % 3 == 0) {
                    tp.setHeight(img.getHeight() / 3);
                    tp.setWidth(img.getWidth() / 4);
                    BufferedImage[] imgsData = new BufferedImage[]{
                        img.getSubimage(tp.getWidth() * 2, tp.getHeight(), tp.getWidth(), tp.getHeight()),
                        img.getSubimage(0, tp.getHeight(), tp.getWidth(), tp.getHeight()),
                        img.getSubimage(tp.getWidth(), 0, tp.getWidth(), tp.getHeight()),
                        img.getSubimage(tp.getWidth(), tp.getHeight() * 2, tp.getWidth(), tp.getHeight()),
                        img.getSubimage(tp.getWidth(), tp.getHeight(), tp.getWidth(), tp.getHeight()),
                        img.getSubimage(tp.getWidth() * 3, tp.getHeight(), tp.getWidth(), tp.getHeight())};
                    data = TextureCreator.getMultiImageData(imgsData);
                } else {
                    data = BufferUtils.createByteBuffer(24);
                    for (int i = 0; i < data.capacity(); i++) {
                        data.put((byte) 255);
                    }
                    data.rewind();
                }
                t.setData(data);
            }
        }

        return t;
    }

    private Texture getAlphaMask(String line) throws Exception {
        int index = line.indexOf(';');
        int id = Integer.parseInt(line.substring(0, index));
        int texCoordIndex = Integer.parseInt(line.substring(index + 1, line.indexOf(';', index + 1)));
        ImageInfo imageInfo = this.imgs.get(id);

        Texture t = this.getTexture(imageInfo);
        t.setTextureCoordsIndex(texCoordIndex);

        return t;
    }

    private Texture[] getColorTextures(String[] lines) throws Exception {
        ArrayList<Texture> textures = new ArrayList();
        for (int i = 1; i < lines.length; i++) {

            String line = lines[i];
            int index = line.indexOf(';');
            int id = Integer.parseInt(line.substring(0, index));
            int texCoordIndex = Integer.parseInt(line.substring(index + 1, line.indexOf(';', index + 1)));

            ImageInfo imageInfo = this.imgs.get(id);
            Texture t = this.getTexture(imageInfo);
            if (t != null) {
                t.setTextureCoordsIndex(texCoordIndex);
                textures.add(t);
            }
        }

        return textures.toArray(new Texture[]{});
    }

    private HashMap<Integer, ImageInfo> readImgs(String[] lines) {
        HashMap<Integer, ImageInfo> imgs = new HashMap();
        for (int i = 1; i < lines.length; i++) {
            String line = lines[i];
            int sIndex = 0;
            int eIndex = line.indexOf(';');
            int id = Integer.parseInt(line.substring(sIndex, eIndex));
            sIndex = eIndex + 1;
            eIndex = line.indexOf(';', sIndex);
            String name = line.substring(sIndex, eIndex);
            sIndex = eIndex + 1;
            eIndex = line.indexOf(';', sIndex);
            int type = this.getImageTypeByName(line.substring(sIndex, eIndex));
            sIndex = eIndex + 1;
            eIndex = line.indexOf(';', sIndex);
            int format = this.getImageFormatByName(line.substring(sIndex, eIndex));;
            File f = null;
            if (line.indexOf('/') == -1) {
                String originalPath = this.sourceFile.getPath();
                f = new File(originalPath.substring(0, originalPath.lastIndexOf(System.getProperty("file.separator"))) + System.getProperty("file.separator") + name);
            } else {
                f = new File(line);
            }
            if (f != null) {
                imgs.put(id, new ImageInfo(f, type, format));
            }
        }
        return imgs;
    }

    private int getImageFormatByName(String name) {
        if (name.compareTo("RGB") == 0) {
            return TextureProperties.FORMAT_RGB;
        } else if (name.compareTo("RGBA") == 0) {
            return TextureProperties.FORMAT_RGBA;
        } else if (name.compareTo("ALPHA") == 0) {
            return TextureProperties.FORMAT_ALPHA;
        } else if (name.compareTo("DEPTH") == 0) {
            return TextureProperties.FORMAT_DEPTH;
        } else if (name.compareTo("LUMINACE") == 0) {
            return TextureProperties.FORMAT_LUMINACE;
        } else {
            return TextureProperties.FORMAT_RGBA;
        }
    }

    private int getImageTypeByName(String name) {
        if (name.compareTo("1D") == 0) {
            return TextureProperties.TEXTURE_1D;
        } else if (name.compareTo("2D") == 0) {
            return TextureProperties.TEXTURE_2D;
        } else if (name.compareTo("3D") == 0) {
            return TextureProperties.TEXTURE_3D;
        } else if (name.compareTo("CUBE") == 0) {
            return TextureProperties.TEXTURE_CUBE_MAP;
        } else {
            return TextureProperties.TEXTURE_2D;
        }
    }

    private String[] separateLines(String part) {
        ArrayList<String> lines = new ArrayList(20);
        int sPoz = 0;
        int kPoz = part.indexOf('\n', sPoz + 1);
        while (sPoz >= 0 && kPoz > 0) {
            lines.add(part.substring(sPoz, kPoz - 1));
            sPoz = kPoz + 1;
            kPoz = part.indexOf('\n', sPoz);
        }
        return lines.toArray(new String[]{});
    }

    private String[] separateParts(String data) {
        ArrayList<String> parts = new ArrayList(5);
        int sPoz = data.indexOf('$') + 1;
        int kPoz = data.indexOf('$', sPoz + 1);
        while (sPoz >= 0 && sPoz < data.length() && kPoz >= 0) {
            parts.add(data.substring(sPoz, kPoz));
            sPoz = kPoz + 1;
            kPoz = data.indexOf('$', sPoz);
        }
        return parts.toArray(new String[]{});
    }

    private String removeComments(String data) {
        boolean continueReading = true;
        while (continueReading) {
            int poziceComentu = data.indexOf("//");
            if (poziceComentu >= 0) {
                int poziceKonceComentu = data.indexOf('\n', poziceComentu);
                data = data.substring(0, poziceComentu) + data.substring(poziceKonceComentu + 1, data.length());
            } else {
                continueReading = false;
            }

        }
        return data;
    }

    private byte[] loadData(File file) {
        try {
            FileInputStream fis = new FileInputStream(file);
            byte[] byteData = new byte[fis.available()];
            fis.read(byteData);
            return byteData;
        } catch (Exception ex) {
            return null;
        }
    }

    private class ImageInfo {

        private File data;
        private int type;
        private int format;

        public ImageInfo(File data, int type, int format) {
            this.data = data;
            this.type = type;
            this.format = format;
        }

        public File getData() {
            return data;
        }

        public int getFormat() {
            return format;
        }

        public int getType() {
            return type;
        }
    }
}
